package com.lucky.aop;

import com.alibaba.fastjson.JSON;
import com.lucky.annotation.SystemLog;
import com.lucky.pojo.vo.User;
import com.lucky.util.RedisCache;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;

/**
 * @Description
 * @Author Jalyson
 * @Version V1.0.0
 * @Since 1.0
 * @Date 2019/6/26
 */

@Aspect
@Component
public class SystemLogAspect {

    //本地异常日志记录对象
    private static final Logger LOGGER = LoggerFactory.getLogger(SystemLogAspect.class);
    private static ExpressionParser parser = new SpelExpressionParser();

    //Service层切点
    //@Pointcut("execution(* com.lucky.controller..*.*(..))  && @annotation(com.lucky.annotation.SystemLog)")
    @Pointcut("@annotation(com.lucky.annotation.SystemLog)")
    public void logAspect() {
    }

    /**
     * 前置通知
     *
     * @param joinPoint 切点
     */
    @Before("logAspect()")
    public void doBefore(JoinPoint joinPoint) {

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        //读取session中的用户
        User user = (User) RedisCache.getData(request.getSession().getId());
        String userName = "访客";
        if (user != null) {
            userName = user.getUsername();
        }
        try {
            //请求的IP
            String ip = request.getRemoteAddr();
            String params = "";
            if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
                for (int i = 0; i < joinPoint.getArgs().length; i++) {
                    params += JSON.toJSONString(joinPoint.getArgs()[i]) + ";";
                }
            }
            String content = getSystemLogs(joinPoint);
            //*========数据库日志=========*//
            LOGGER.info("保存日志到数据库：日志内容:{}", content);
            //*========数据库日志结束=========*//

            LOGGER.info("用户:{}({}),访问方法:{},参数:{}", userName, ip, joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName(), params);

        } catch (Exception e) {
            //记录本地异常日志
            LOGGER.error("异常信息:{}", e.getMessage());
        }
    }

    /**
     * 异常通知 用于拦截service层记录异常日志
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut = "logAspect()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        //读取session中的用户
        User user = (User) RedisCache.getData(request.getSession().getId());
        String userName = "访客";
        if (user != null) {
            userName = user.getUsername();
        }
        try {
            //获取请求ip
            String ip = request.getRemoteAddr();
            //获取用户请求方法的参数并序列化为JSON格式字符串
            String params = "";
            if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
                for (int i = 0; i < joinPoint.getArgs().length; i++) {
                    params += JSON.toJSONString(joinPoint.getArgs()[i]) + ";";
                }
            }
            String content = getSystemLogs(joinPoint);
            LOGGER.info("保存日志到数据库：日志内容:{}", content);
            LOGGER.error("用户:{}({}),异常方法:{}异常代码:{}异常信息:{}参数:{}", userName, ip, joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName(),
                         e.getClass().getName(), e.getMessage(), params);
        } catch (Exception ex) {
            //记录本地异常日志
            LOGGER.error("异常信息:{}", ex.getMessage());
        }
    }

    /**
     * 获取注解中对方法的描述信息 用于service层注解
     *
     * @param joinPoint 切点
     * @return 方法描述
     * @throws Exception
     */
    public static String getSystemLogs(JoinPoint joinPoint)
            throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        String description = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    String expressionStr = method.getAnnotation(SystemLog.class).description();
                    description = calSpelVal(method, expressionStr, arguments);
                    break;
                }
            }
        }
        return description;
    }

    /**
     * 计算spe表达式值
     * @param method method
     * @param expressionStr expressionStr
     * @param arguments arguments
     * @return
     */
    private static String calSpelVal(Method method, String expressionStr, Object[] arguments) {
        Parameter[] parameters = method.getParameters();
        EvaluationContext ctx = new StandardEvaluationContext();
        int i = 0;
        for (Parameter p : parameters) {
            ctx.setVariable(p.getName(), arguments[i]);
        }
        String description = expressionStr;
        Expression expression = parser.parseExpression(expressionStr);
        Object value = expression.getValue(ctx);
        if (value != null) {
            description = value.toString();
        }
        return description;
    }

    public static void main(String[] args) {
        //Spring 表达式语言(简称SpEL)：是一个支持运行时查询和操作对象图的强大的表达式语言。
        //这个表达式很有用，很多程序都会用到这种表达式，像工作流
        /*
            通过 SpEL 可以实现：
            通过 bean 的 id 对 bean 进行引用。
            调用方式以及引用对象中的属性。
            计算表达式的值。
            正则表达式的匹配。
        */
        //https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/expressions.html
        EvaluationContext context = new StandardEvaluationContext();
        ExpressionParser parser = new SpelExpressionParser();

        Map inventorInfo = (Map) parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context);
        System.out.println("{name:'Nikola',dob:'10-July-1856'} =======================> "+inventorInfo);

        int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context);
        System.out.println("new int[4] =======================> "+numbers1);

        String c = parser.parseExpression("'abc'.substring(2, 3)").getValue(String.class);
        System.out.println("'abc'.substring(2, 3) =======================> "+c);
 
        boolean trueValue1 = parser.parseExpression("2 == 2").getValue(Boolean.class);
        System.out.println("2 == 2  =======================> "+trueValue1);

        boolean trueValue2 = parser.parseExpression("'5.00' matches '\\^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
        System.out.println("'5.00' matches '\\^-?\\d+(\\.\\d{2})?$' =======================> "+trueValue2);

        boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class);
        System.out.println("true and false =======================> "+falseValue);

        String message = parser.parseExpression("'Hello World'.concat('!')").getValue(String.class);
        System.out.println("'Hello World'.concat('!') =======================> "+message);

        String message2 = parser.parseExpression("'Hello World'+'!'").getValue(String.class);
        System.out.println("'Hello World'+'!' =======================> "+message2);

        Double dVal = parser.parseExpression("T(java.lang.Math).random() * 100.0").getValue(Double.class);
        System.out.println("T(java.lang.Math).random() * 100.0 =======================> "+dVal);

        String expressionStr = "${user.username}+' 密码是：'+#user.password";
        Expression expression = parser.parseExpression(expressionStr);
        User user = new User();
        user.setUsername("Lucky");
        user.setPassword("123456");
        context.setVariable("user", user);
        Object value = expression.getValue(context);

        System.out.println(value);

        //applicationContext
        //String expressionStr = "#userService.findAll()";
    }
}
